package org.xliu.cs.openproject.codegenerator.pojo;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

/**
 * 包扫描器
 * 通过反射扫描指定包下的所有类和枚举类型
 */
public class PackageScanner {
    
    private static final Logger logger = LogManager.getLogger(PackageScanner.class);

    /**
     * 扫描指定包下的所有类和枚举
     *
     * @param packageName 包名
     * @return 包含JavaClass和JavaEnum的列表
     */
    public static List<JavaElement> scanPackage(String packageName) {
        List<JavaElement> result = new ArrayList<>();
        try {
            logger.info("开始扫描包: {}", packageName);
            
            // 获取类加载器
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            String path = packageName.replace('.', '/');
            
            // 获取包路径下的所有资源
            Enumeration<URL> resources = classLoader.getResources(path);
            List<File> dirs = new ArrayList<>();
            List<String> jars = new ArrayList<>();
            
            while (resources.hasMoreElements()) {
                URL resource = resources.nextElement();
                String protocol = resource.getProtocol();
                
                if ("file".equals(protocol)) {
                    dirs.add(new File(resource.getFile()));
                } else if ("jar".equals(protocol)) {
                    String jarPath = resource.getPath().substring(5, resource.getPath().indexOf("!"));
                    jars.add(jarPath);
                }
            }
            
            // 扫描目录中的类文件
            for (File directory : dirs) {
                logger.debug("扫描目录: {}", directory.getAbsolutePath());
                result.addAll(findClassesInDirectory(directory, packageName));
            }
            
            // 扫描jar包中的类文件
            for (String jarPath : jars) {
                logger.debug("扫描JAR文件: {}", jarPath);
                result.addAll(findClassesInJar(jarPath, packageName));
            }
            
            logger.info("包扫描完成，共找到 {} 个项目", result.size());
            
        } catch (IOException e) {
            logger.error("扫描包时发生IO异常: {}", e.getMessage(), e);
        }
        
        return result;
    }
    
    /**
     * 从目录中查找类文件
     *
     * @param directory   目录
     * @param packageName 包名
     * @return 包含JavaClass和JavaEnum的列表
     */
    private static List<JavaElement> findClassesInDirectory(File directory, String packageName) {
        List<JavaElement> result = new ArrayList<>();
        
        if (!directory.exists()) {
            logger.warn("目录不存在: {}", directory.getAbsolutePath());
            return result;
        }
        
        File[] files = directory.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    // 递归扫描子目录
                    result.addAll(findClassesInDirectory(file, packageName + "." + file.getName()));
                } else if (file.getName().endsWith(".class")) {
                    // 处理类文件
                    String className = packageName + '.' + file.getName().substring(0, file.getName().length() - 6);
                    JavaElement obj = processClassOrEnum(className);
                    if (obj != null) {
                        result.add(obj);
                    }
                }
            }
        }
        
        return result;
    }
    
    /**
     * 从Jar文件中查找类文件
     *
     * @param jarPath     Jar文件路径
     * @param packageName 包名
     * @return 包含JavaClass和JavaEnum的列表
     */
    private static List<JavaElement> findClassesInJar(String jarPath, String packageName) {
        List<JavaElement> result = new ArrayList<>();
        String packagePath = packageName.replace('.', '/');
        
        try (JarFile jarFile = new JarFile(jarPath)) {
            Enumeration<JarEntry> entries = jarFile.entries();
            
            while (entries.hasMoreElements()) {
                JarEntry entry = entries.nextElement();
                String name = entry.getName();
                
                if (name.endsWith(".class") && name.startsWith(packagePath)) {
                    String className = name.substring(0, name.length() - 6).replace('/', '.');
                    JavaElement obj = processClassOrEnum(className);
                    if (obj != null) {
                        result.add(obj);
                    }
                }
            }
        } catch (IOException e) {
            logger.error("读取JAR文件时发生异常: {}", jarPath, e);
        }
        
        return result;
    }
    
    /**
     * 处理类或枚举
     *
     * @param className 类名
     * @return JavaClass或JavaEnum对象
     */
    private static JavaElement processClassOrEnum(String className) {
        try {
            logger.debug("处理类: {}", className);
            
            // 加载类
            Class<?> clazz = Class.forName(className);
            
            // 处理枚举类型
            if (clazz.isEnum()) {
                return processEnum(clazz);
            }
            
            // 跳过接口和注解
            if (clazz.isInterface() || clazz.isAnnotation()) {
                logger.debug("跳过特殊类型类: {}", className);
                return null;
            }
            
            // 处理普通类
            return processClass(clazz);
            
        } catch (ClassNotFoundException e) {
            logger.error("无法加载类: {}", className, e);
        } catch (NoClassDefFoundError e) {
            logger.error("找不到类定义: {}", className, e);
        } catch (UnsupportedOperationException e) {
            logger.error("类 {} 包含不支持的字段类型: {}", className, e.getMessage());
            throw e;
        }
        
        return null;
    }
    
    /**
     * 处理类，提取字段信息
     *
     * @param clazz 类
     * @return JavaClass对象
     */
    private static JavaClass processClass(Class<?> clazz) {
        String className = clazz.getSimpleName();
        try {
            logger.debug("处理类: {}", clazz.getName());
            
            // 创建JavaClass对象
            JavaClass javaClass = new JavaClass(className);
            
            // 获取所有字段（包括父类字段）
            Field[] fields = clazz.getDeclaredFields();
            logger.info("类 {} 包含 {} 个字段", clazz.getName(), fields.length);
            
            for (Field field : fields) {
                // 处理每个字段
                JavaField javaField = processField(field, clazz.getPackage().getName());
                javaClass.addField(javaField);
            }
            
            return javaClass;
        } catch (Exception e) {
            logger.error("处理类 {} 时发生错误: {}", className, e.getMessage(), e);
        }
        
        return null;
    }
    
    /**
     * 处理枚举类，提取枚举常量信息
     *
     * @param clazz 枚举类
     * @return JavaEnum对象
     */
    private static JavaEnum processEnum(Class<?> clazz) {
        String enumName = clazz.getSimpleName();
        try {
            logger.debug("处理枚举类: {}", clazz.getName());
            
            // 创建JavaEnum对象
            JavaEnum javaEnum = new JavaEnum(enumName);
            
            // 获取所有枚举常量
            Object[] enumConstants = clazz.getEnumConstants();
            if (enumConstants != null) {
                for (Object constant : enumConstants) {
                    javaEnum.addConstant(constant.toString());
                }
            }
            
            logger.info("枚举 {} 包含 {} 个常量", clazz.getName(), enumConstants != null ? enumConstants.length : 0);
            return javaEnum;
        } catch (Exception e) {
            logger.error("处理枚举 {} 时发生错误: {}", enumName, e.getMessage(), e);
        }
        
        return null;
    }
    
    /**
     * 处理字段，提取字段信息
     *
     * @param field 字段
     * @param packagePath 当前类所在的包路径
     * @return JavaField对象
     */
    private static JavaField processField(Field field, String packagePath) {
        String fieldName = field.getName();
        Class<?> fieldType = field.getType();
        String fieldTypeName = fieldType.getSimpleName();
        
        logger.debug("处理字段: {}.{}", field.getDeclaringClass().getName(), fieldName);
        
        // 判断是否为List类型
        boolean isList = isListType(fieldType);
        // 判断是否为数组类型
        boolean isArray = isArrayType(fieldType);
        // 判断是否为枚举类型
        boolean isEnum = isEnumType(fieldType);
        String listElementType = null;
        
        if (isList) {
            // 检查是否为嵌套的List类型，如List<List<String>>
            if (isNestedListType(field)) {
                String errorMsg = String.format("不支持嵌套的List类型: %s.%s (类型: List<List<...>>)",
                    field.getDeclaringClass().getSimpleName(), fieldName);
                logger.error(errorMsg);
                throw new UnsupportedOperationException(errorMsg);
            }
            // 获取List的泛型类型
            listElementType = getListElementType(field);
            fieldTypeName = "List<" + listElementType + ">";
            logger.debug("检测到List类型字段: {}, 元素类型: {}", fieldName, listElementType);
        } else if (isArray) {
            // 获取数组的元素类型
            listElementType = getArrayElementType(fieldType);
            // 检查是否为嵌套的数组类型
            if (isNestedArrayType(fieldType)) {
                String errorMsg = String.format("不支持嵌套的数组类型: %s.%s (类型: %s[])", 
                    field.getDeclaringClass().getSimpleName(), fieldName, listElementType + "[]");
                logger.error(errorMsg);
                throw new UnsupportedOperationException(errorMsg);
            }
            fieldTypeName = listElementType + "[]";
            logger.debug("检测到数组类型字段: {}, 元素类型: {}", fieldName, listElementType);
        } else if (isEnum) {
            fieldTypeName = fieldType.getSimpleName();
            logger.debug("检测到枚举类型字段: {}", fieldName);
        }

        // 转换为C++类型
        String cppType;
        if (isList || isArray) {
            String elementCppType = BasicTypeMapping.getCppTypeForJavaType(listElementType);
            cppType = "std::vector<" + elementCppType + ">";
        } else {
            cppType = BasicTypeMapping.getCppTypeForJavaType(fieldTypeName);
        }
        
        // 生成首字母大写的字段名
        String capitalizedName = capitalize(fieldName);
        
        // 创建JavaField对象
        if (isList || isArray) {
            return new JavaField(fieldName, fieldTypeName, cppType, capitalizedName, listElementType);
        } else {
            // 对于非基本类型且在同一包中的自定义类，记录其包信息
            if (!isPrimitiveType(fieldTypeName) && isInSamePackage(fieldType, packagePath)) {
                return new JavaField(fieldName, fieldTypeName, cppType, capitalizedName);
            }
            return new JavaField(fieldName, fieldTypeName, cppType, capitalizedName);
        }
    }
    
    /**
     * 判断类型是否为基本类型
     *
     * @param typeName 类型名称
     * @return 是否为基本类型
     */
    private static boolean isPrimitiveType(String typeName) {
        return "int".equals(typeName) || "Integer".equals(typeName) ||
               "long".equals(typeName) || "Long".equals(typeName) ||
               "float".equals(typeName) || "Float".equals(typeName) ||
               "double".equals(typeName) || "Double".equals(typeName) ||
               "boolean".equals(typeName) || "Boolean".equals(typeName) ||
               "String".equals(typeName) || "char".equals(typeName) ||
               "byte".equals(typeName) || "short".equals(typeName);
    }
    
    /**
     * 判断类是否在同一包中
     *
     * @param clazz 类
     * @param packagePath 包路径
     * @return 是否在同一包中
     */
    private static boolean isInSamePackage(Class<?> clazz, String packagePath) {
        Package pkg = clazz.getPackage();
        return pkg != null && pkg.getName().equals(packagePath);
    }
    
    /**
     * 判断字段类型是否为List
     *
     * @param fieldType 字段类型
     * @return 是否为List类型
     */
    private static boolean isListType(Class<?> fieldType) {
        return java.util.List.class.isAssignableFrom(fieldType);
    }
    
    /**
     * 判断字段类型是否为数组
     *
     * @param fieldType 字段类型
     * @return 是否为数组类型
     */
    private static boolean isArrayType(Class<?> fieldType) {
        return fieldType.isArray();
    }
    
    /**
     * 判断字段类型是否为枚举
     *
     * @param fieldType 字段类型
     * @return 是否为枚举类型
     */
    private static boolean isEnumType(Class<?> fieldType) {
        return fieldType.isEnum();
    }
    
    /**
     * 检查List字段是否为嵌套的List类型（例如List<List<String>>）
     *
     * @param field 字段
     * @return 是否为嵌套的List类型
     */
    private static boolean isNestedListType(Field field) {
        Type genericType = field.getGenericType();
        if (genericType instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) genericType;
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
            if (actualTypeArguments.length > 0) {
                Type elementType = actualTypeArguments[0];
                // 检查元素类型是否也是ParameterizedType（即泛型类型）
                if (elementType instanceof ParameterizedType) {
                    Type rawType = ((ParameterizedType) elementType).getRawType();
                    // 检查元素类型是否为List的子类型
                    return rawType instanceof Class && List.class.isAssignableFrom((Class<?>) rawType);
                }
            }
        }
        return false;
    }
    
    /**
     * 检查数组字段是否为嵌套的数组类型（例如String[][]）
     *
     * @param fieldType 字段类型
     * @return 是否为嵌套的数组类型
     */
    private static boolean isNestedArrayType(Class<?> fieldType) {
        Class<?> componentType = fieldType.getComponentType();
        // 如果组件类型仍然是数组类型，则为嵌套数组
        return componentType != null && componentType.isArray();
    }
    
    /**
     * 获取List字段的泛型元素类型
     *
     * @param field 字段
     * @return 元素类型名称
     */
    private static String getListElementType(Field field) {
        Type genericType = field.getGenericType();
        if (genericType instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) genericType;
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
            if (actualTypeArguments.length > 0) {
                Type elementType = actualTypeArguments[0];
                if (elementType instanceof Class) {
                    return ((Class<?>) elementType).getSimpleName();
                } else {
                    return elementType.getTypeName();
                }
            }
        }
        return null;
    }
    
    /**
     * 获取数组字段的元素类型
     *
     * @param fieldType 字段类型
     * @return 元素类型名称
     */
    private static String getArrayElementType(Class<?> fieldType) {
        Class<?> componentType = fieldType.getComponentType();
        if (componentType != null) {
            // 对于多维数组，获取最内层的元素类型
            while (componentType.isArray()) {
                componentType = componentType.getComponentType();
            }
            return componentType.getSimpleName();
        }
        return null;
    }
    
    /**
     * 生成字段的首字母大写名称
     *
     * @param name 字段名
     * @return 首字母大写字段名
     */
    private static String capitalize(String name) {
        if (name == null || name.isEmpty()) {
            return name;
        }
        return Character.toUpperCase(name.charAt(0)) + name.substring(1);
    }
}